﻿#include "InternalWindowFrameless.h"
#include <QRect>
#include <QRubberBand>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QApplication>
#include <QDebug>
#include "BaseModule/IBaseSignal.h"

int InternalWindowCursorPosCalculator::m_nBorderWidth = 5;
int InternalWindowCursorPosCalculator::m_nTitleHeight = 30;

/***** InternalWindowCursorPosCalculator *****/
InternalWindowCursorPosCalculator::InternalWindowCursorPosCalculator()
{
    reset();
}

void InternalWindowCursorPosCalculator::reset()
{
    m_bOnEdges = false;
    m_bOnLeftEdge = false;
    m_bOnRightEdge = false;
    m_bOnTopEdge = false;
    m_bOnBottomEdge = false;
    m_bOnTopLeftEdge = false;
    m_bOnBottomLeftEdge = false;
    m_bOnTopRightEdge = false;
    m_bOnBottomRightEdge = false;
}

void InternalWindowCursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect)
{
    int globalMouseX = gMousePos.x();
    int globalMouseY = gMousePos.y();

    int frameX = frameRect.x();
    int frameY = frameRect.y();

    int frameWidth = frameRect.width();
    int frameHeight = frameRect.height();

    m_bOnLeftEdge = (globalMouseX >= frameX &&
                     globalMouseX <= frameX + m_nBorderWidth);

    m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth &&
                      globalMouseX <= frameX + frameWidth);

    // m_bOnTopEdge = (globalMouseY >= frameY &&
    //                 globalMouseY <= frameY + m_nBorderWidth);
    m_bOnTopEdge = (globalMouseY >= frameY &&
                    globalMouseY <= frameY + 2);

    m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth &&
                       globalMouseY <= frameY + frameHeight);

    m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
    m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
    m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
    m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;

    m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge /*|| m_bOnTopEdge*/ || m_bOnBottomEdge;
}

/***** InternalWindowWidgetData *****/
InternalWindowWidgetData::InternalWindowWidgetData(InternalWindowFramelessPrivate* _d, QWidget* pTopLevelWidget)
{
    d = _d;
    m_pWidget = pTopLevelWidget;
    m_bLeftButtonPressed = false;
    m_bCursorShapeChanged = false;
    m_bLeftButtonTitlePressed = false;
    m_pRubberBand = NULL;

    m_windowFlags = m_pWidget->windowFlags();
    m_pWidget->setMouseTracking(true);
    m_pWidget->setAttribute(Qt::WA_Hover, true);

    updateRubberBandStatus();
}

InternalWindowWidgetData::~InternalWindowWidgetData()
{
    m_pWidget->setMouseTracking(false);
    m_pWidget->setWindowFlags(m_windowFlags);
    m_pWidget->setAttribute(Qt::WA_Hover, false);

    delete m_pRubberBand;
    m_pRubberBand = NULL;
}

bool InternalWindowWidgetData::handleWidgetEvent(QEvent* event)
{
    switch (event->type())
    {
    case QEvent::MouseButtonPress:
        return (handleMousePressEvent(static_cast<QMouseEvent*>(event)));

    case QEvent::MouseButtonRelease:
        return (handleMouseReleaseEvent(static_cast<QMouseEvent*>(event)));

    case QEvent::MouseMove:
        return (handleMouseMoveEvent(static_cast<QMouseEvent*>(event)));

    case QEvent::Leave:
        return (handleLeaveEvent(static_cast<QMouseEvent*>(event)));

    case QEvent::HoverMove:
        return (handleHoverMoveEvent(static_cast<QHoverEvent*>(event)));

    case QEvent::MouseButtonDblClick:
        return (handleDoubleClickedMouseEvent(static_cast<QMouseEvent*>(event)));

    default:
        break;
    }
    return (false);
}
void InternalWindowWidgetData::updateRubberBandStatus()
{
    if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize)
    {
        if (NULL == m_pRubberBand)
        {
            m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
        }
    }
    else
    {
        delete m_pRubberBand;
        m_pRubberBand = NULL;
    }
}

void InternalWindowWidgetData::updateCursorShape(const QPoint& gMousePos)
{
    if (m_pWidget->isFullScreen() || m_pWidget->isMaximized())
    {
        if (m_bCursorShapeChanged)
        {
            // m_pWidget->unsetCursor();
            emit IBaseSignalInstance->signal_resetWidgetCursor();
        }
        return;
    }

    m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());

    if (m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge)
    {
        // m_pWidget->setCursor(Qt::SizeFDiagCursor);
        emit IBaseSignalInstance->signal_updateWidgetCursor(Qt::SizeFDiagCursor);
        m_bCursorShapeChanged = true;
    }
    else if (m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge)
    {
        // m_pWidget->setCursor(Qt::SizeBDiagCursor);
        emit IBaseSignalInstance->signal_updateWidgetCursor(Qt::SizeBDiagCursor);
        m_bCursorShapeChanged = true;
    }
    else if (m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge)
    {
        // m_pWidget->setCursor(Qt::SizeHorCursor);
        emit IBaseSignalInstance->signal_updateWidgetCursor(Qt::SizeHorCursor);
        m_bCursorShapeChanged = true;
    }
    else if (/*m_moveMousePos.m_bOnTopEdge ||*/ m_moveMousePos.m_bOnBottomEdge)
    {
        // m_pWidget->setCursor(Qt::SizeVerCursor);
        emit IBaseSignalInstance->signal_updateWidgetCursor(Qt::SizeVerCursor);
        m_bCursorShapeChanged = true;
    }
    else if (m_moveMousePos.m_bOnTopEdge)
    {
        emit IBaseSignalInstance->signal_updateWidgetCursor(Qt::SizeAllCursor);
        m_bCursorShapeChanged = true;
    }
    else
    {
        if (m_bCursorShapeChanged)
        {
            // m_pWidget->unsetCursor();
            emit IBaseSignalInstance->signal_resetWidgetCursor();
            m_bCursorShapeChanged = false;
        }
    }
}

void InternalWindowWidgetData::resizeWidget(const QPoint& gMousePos)
{
    QRect origRect;

    if (d->m_bRubberBandOnResize)
    {
        origRect = m_pRubberBand->frameGeometry();
    }
    else
    {
        origRect = m_pWidget->frameGeometry();
    }

    int left = origRect.left();
    int top = origRect.top();
    int right = origRect.right();
    int bottom = origRect.bottom();

    origRect.getCoords(&left, &top, &right, &bottom);

    int minWidth = m_pWidget->minimumWidth();
    int minHeight = m_pWidget->minimumHeight();

    if (m_pressedMousePos.m_bOnTopLeftEdge)
    {
        left = gMousePos.x();
        top = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnBottomLeftEdge)
    {
        left = gMousePos.x();
        bottom = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnTopRightEdge)
    {
        right = gMousePos.x();
        top = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnBottomRightEdge)
    {
        right = gMousePos.x();
        bottom = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnLeftEdge)
    {
        left = gMousePos.x();
    }
    else if (m_pressedMousePos.m_bOnRightEdge)
    {
        right = gMousePos.x();
    }
    else if (m_pressedMousePos.m_bOnTopEdge)
    {
        top = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnBottomEdge)
    {
        bottom = gMousePos.y();
    }

    QRect newRect(QPoint(left, top), QPoint(right, bottom));

    if (newRect.isValid())
    {
        if (minWidth > newRect.width())
        {
            if (left != origRect.left())
            {
                newRect.setLeft(origRect.left());
            }
            else
            {
                newRect.setRight(origRect.right());
            }
        }
        if (minHeight > newRect.height())
        {
            if (top != origRect.top())
            {
                newRect.setTop(origRect.top());
            }
            else
            {
                newRect.setBottom(origRect.bottom());
            }
        }

        if (d->m_bRubberBandOnResize)
        {
            m_pRubberBand->setGeometry(newRect);
        }
        else
        {
            m_pWidget->setGeometry(newRect);
        }
    }
}

void InternalWindowWidgetData::moveWidget(const QPoint& gMousePos)
{
    if (d->m_bRubberBandOnMove)
    {
        m_pRubberBand->move(gMousePos - m_ptDragPos);
    }
    else
    {
        m_pWidget->move(gMousePos - m_ptDragPos);
    }
}

static QPoint GlobalToRelative(const QPoint& pos, const QWidget* widget)
{
    QPoint point = pos;
    auto parentWidget = widget->parentWidget();
    if (parentWidget != nullptr)
    {
        point = parentWidget->mapFromGlobal(point);
    }
    return point;
}

bool InternalWindowWidgetData::handleMousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_bLeftButtonPressed = true;
        m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight;

        QRect frameRect = m_pWidget->frameGeometry();
        m_pressedMousePos.recalculate(GlobalToRelative(event->globalPos(), m_pWidget), frameRect);

        m_ptDragPos = GlobalToRelative(event->globalPos(), m_pWidget) - frameRect.topLeft();

        if (m_pressedMousePos.m_bOnEdges)
        {
            if (m_pWidget->isMaximized())
            {
                // 窗口在最大化状态时，点击边界不做任何处理
                return (false);
            }
            if (d->m_bRubberBandOnResize)
            {
                m_pRubberBand->setGeometry(frameRect);
                m_pRubberBand->show();
                return (true);
            }
        }
        else if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed)
        {
            if (m_pWidget->isMaximized())
            {
                // 窗口在最大化状态时，点击标题栏不做任何处理
                return (false);
            }
            m_pRubberBand->setGeometry(frameRect);
            m_pRubberBand->show();
            return (true);
        }
    }
    return (false);
}

bool InternalWindowWidgetData::handleMouseReleaseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_bLeftButtonPressed = false;
        m_bLeftButtonTitlePressed = false;
        m_pressedMousePos.reset();
        if (m_pRubberBand && m_pRubberBand->isVisible())
        {
            m_pRubberBand->hide();
            m_pWidget->setGeometry(m_pRubberBand->geometry());
            return (true);
        }
    }
    return (false);
}

bool InternalWindowWidgetData::handleMouseMoveEvent(QMouseEvent* event)
{
    if (m_bLeftButtonPressed)
    {
        if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges)
        {
            if (m_pWidget->isMaximized())
            {
                // 窗口在最大化状态时，点击边界不做任何处理
                return (false);
            }
            resizeWidget(GlobalToRelative(event->globalPos(), m_pWidget));
            return (true);
        }
        else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed)
        {
            if (m_pWidget->isMaximized())
            {
                // 先求出窗口到鼠标的相对位置
                QRect normalGeometry = m_pWidget->normalGeometry();
                m_pWidget->showNormal();
                QPoint p = GlobalToRelative(event->globalPos(), m_pWidget);
                p.ry() -= 10;
                p.rx() -= (normalGeometry.width() / 2);
                m_pWidget->move(p);
                // 这时要重置m_ptDragPos
                m_ptDragPos = QPoint(normalGeometry.width() / 2, 10);
                return (true);
            }
            moveWidget(GlobalToRelative(event->globalPos(), m_pWidget));
            return (true);
        }
        return (false);
    }
    else if (d->m_bWidgetResizable)
    {
        updateCursorShape(GlobalToRelative(event->globalPos(), m_pWidget));
    }
    return (false);
}

bool InternalWindowWidgetData::handleLeaveEvent(QEvent* event)
{
    Q_UNUSED(event)
    if (!m_bLeftButtonPressed)
    {
        // m_pWidget->unsetCursor();
        emit IBaseSignalInstance->signal_resetWidgetCursor();
        return (true);
    }
    return (false);
}

bool InternalWindowWidgetData::handleHoverMoveEvent(QHoverEvent* event)
{
    if (d->m_bWidgetResizable)
    {
        updateCursorShape(GlobalToRelative(m_pWidget->mapToGlobal(event->pos()), m_pWidget));
    }
    return (false);
}

bool InternalWindowWidgetData::handleDoubleClickedMouseEvent(QMouseEvent* event)
{
    // if (event->button() == Qt::LeftButton) {
    //     if (m_pWidget) {
    //         IdaMainWindow* mainwindow = qobject_cast<IdaMainWindow*>(m_pWidget);
    //         if (mainwindow) {
    //             if (mainwindow->windowButtonFlags() & Qt::WindowMaximizeButtonHint) {
    //                 //在最大化按钮显示时才进行shownormal处理
    //                 bool titlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight;
    //                 if (titlePressed) {
    //                     if (m_pWidget->isMaximized()) {
    //                         m_pWidget->showNormal();
    //                     }
    //                     else {
    //                         m_pWidget->showMaximized();
    //                     }
    //                     return (true);
    //                 }
    //             }
    //         }
    //     }
    // }
    return (false);
}

InternalWindowFrameless::InternalWindowFrameless(QObject* parent)
    : QObject(parent),
      d(new InternalWindowFramelessPrivate())
{
    d->m_bWidgetMovable = true;
    d->m_bWidgetResizable = true;
    d->m_bRubberBandOnResize = false;
    d->m_bRubberBandOnMove = false;
    if (parent)
    {
        QWidget* w = qobject_cast<QWidget*>(parent);
        if (w)
        {
            w->setWindowFlags(w->windowFlags() | Qt::FramelessWindowHint);
            setWidgetMovable(true);       // 设置窗体可移动
            setWidgetResizable(true);     // 设置窗体可缩放
            setRubberBandOnMove(false);   // 设置橡皮筋效果-可移动
            setRubberBandOnResize(false); // 设置橡皮筋效果-可缩放
            activateOn(w);                // 激活当前窗体
        }
    }
}

InternalWindowFrameless::~InternalWindowFrameless()
{
    QList<QWidget*> keys = d->m_widgetDataHash.keys();
    int size = keys.size();

    for (int i = 0; i < size; ++i)
    {
        delete d->m_widgetDataHash.take(keys[i]);
    }

    delete d;
}

bool InternalWindowFrameless::eventFilter(QObject* obj, QEvent* event)
{
    switch (event->type())
    {
    case QEvent::MouseMove:
    case QEvent::HoverMove:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
    case QEvent::Leave:
    {
        InternalWindowWidgetData* data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
        if (data)
        {
            return (data->handleWidgetEvent(event));
        }
        break;
    }

    default:
        break;
    }
    return (QObject::eventFilter(obj, event));
}

void InternalWindowFrameless::activateOn(QWidget* topLevelWidget)
{
    if (!d->m_widgetDataHash.contains(topLevelWidget))
    {
        InternalWindowWidgetData* data = new InternalWindowWidgetData(d, topLevelWidget);
        d->m_widgetDataHash.insert(topLevelWidget, data);

        topLevelWidget->installEventFilter(this);
    }
}

void InternalWindowFrameless::removeFrom(QWidget* topLevelWidget)
{
    InternalWindowWidgetData* data = d->m_widgetDataHash.take(topLevelWidget);

    if (data)
    {
        topLevelWidget->removeEventFilter(this);
        delete data;
    }
}

void InternalWindowFrameless::setRubberBandOnMove(bool movable)
{
    d->m_bRubberBandOnMove = movable;
    QList<InternalWindowWidgetData*> list = d->m_widgetDataHash.values();

    foreach (InternalWindowWidgetData* data, list)
    {
        data->updateRubberBandStatus();
    }
}

void InternalWindowFrameless::setWidgetMovable(bool movable)
{
    d->m_bWidgetMovable = movable;
}

void InternalWindowFrameless::setWidgetResizable(bool resizable)
{
    d->m_bWidgetResizable = resizable;
}

void InternalWindowFrameless::setRubberBandOnResize(bool resizable)
{
    d->m_bRubberBandOnResize = resizable;
    QList<InternalWindowWidgetData*> list = d->m_widgetDataHash.values();

    foreach (InternalWindowWidgetData* data, list)
    {
        data->updateRubberBandStatus();
    }
}

void InternalWindowFrameless::setBorderWidth(int width)
{
    if (width > 0)
    {
        InternalWindowCursorPosCalculator::m_nBorderWidth = width;
    }
}

void InternalWindowFrameless::setTitleHeight(int height)
{
    if (height > 0)
    {
        InternalWindowCursorPosCalculator::m_nTitleHeight = height;
    }
}

bool InternalWindowFrameless::widgetMovable()
{
    return (d->m_bWidgetMovable);
}

bool InternalWindowFrameless::widgetResizable()
{
    return (d->m_bWidgetResizable);
}

bool InternalWindowFrameless::rubberBandOnMove()
{
    return (d->m_bRubberBandOnMove);
}

bool InternalWindowFrameless::rubberBandOnResisze()
{
    return (d->m_bRubberBandOnResize);
}

uint InternalWindowFrameless::borderWidth()
{
    return (InternalWindowCursorPosCalculator::m_nBorderWidth);
}

uint InternalWindowFrameless::titleHeight()
{
    return (InternalWindowCursorPosCalculator::m_nTitleHeight);
}

InternalWindowWidgetData* InternalWindowFrameless::getWidgetData(QWidget* widget)
{
    InternalWindowWidgetData* data = d->m_widgetDataHash.value(widget);
    return data;
}
